package com.blog.cloud.security.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWT;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import jakarta.servlet.http.HttpServletRequest;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;

/**
 * JWT工具类
 *
 * @author 龙茶清欢
 * @since 2022-11-06 12:30:55
 */
public class JwtTokenUtil {

    /**
     * 生成token
     */
    public static String createToken(Long userId) {
        String token = JWT.create()
                .setPayload("sub", "1234567890")
                .setPayload("name", "looly")
                .setPayload("admin", true)

                // 设置签发时间
                .setIssuedAt(DateUtil.date())
                // 设置过期时间，1天有效期，使用 DateUtil.offsetDay
                // .setExpiresAt(DateUtil.offsetDay(DateUtil.date(), 1))
                // 1分钟有效，使用 DateUtil.offsetMinute
                .setExpiresAt(DateUtil.offsetHour(DateUtil.date(), 1))
                .setKey(key)
                .sign();

        Map<String, Object> header = new HashMap<>(2);
        header.put("typ","JWT");
        header.put("alg", "HS256");
        Map<String, Object> claim = new HashMap<>(1);
        claim.put("userId",userId);
        return Jwts.builder()
                .setHeader(header)
                // 唯一ID标识
                .setId(UUID.randomUUID().toString())
                // token签发人
                .setIssuer(TokenConstant.TOKEN_ISSUER)
                // 签发时间
                .setIssuedAt(new Date())
                // 设置过期时间
                .setExpiration(new Date(System.currentTimeMillis() + TokenConstant.EXPIRATION_DATE))
                // 传输私密数据
                .setClaims(claim)
                // 设置签名参数，第一个为加密算法，第二个为签名密钥
                .signWith(SignatureAlgorithm.HS256,generalKey())
                // 生成token
                .compact();
    }

    /**
     * 解析token
     */
    public static Long parseToken(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(generalKey())
                .parseClaimsJws(token)
                .getBody();
        return JSON.parseObject(claims.get("userId").toString(), Long.class);
    }

    /**
     * 将设置的签名密钥进行Base64加密
     */
    private static SecretKey generalKey() {
        byte[] decode = Base64.getDecoder().decode(TokenConstant.SIGN_KEY);
        return new SecretKeySpec(decode, 0, decode.length, "AES");
    }

    /**
     * 从请求头中获取token
     */
    public static String getToken(HttpServletRequest request) {
        // 普通登录token
        String token = request.getHeader(TokenConstant.AUTHORIZATION);
        if (StrUtil.isBlank(token)) {
            // 三方授权登录token
            token = request.getHeader(TokenConstant.ACCESS_TOKEN);
        }
        return token;
    }

    /**
     * 验证token是否自动过期
     */
    public static boolean verifyToken(String token) {
        try {
            // 如果token已经过期，parseClaimsJws(token)方法将抛出一个ExpiredJwtException异常。
            // 如果解析成功，则表明token未过期，但不代表token有效
            // 而当我们进一步通过redis来控制token时，我们则可以灵活的随时控制token的有效性
            Jwts.parser().setSigningKey(generalKey()).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            // token已过期，则表明token一定无效
            return false;
        }
    }
}

// 静态属性不能使用属性注入
// 设置的明文签名密钥建议先加密后在使用，可以使用Base64进行加密
